#include <iostream>
#include <fstream>
#include <list>
#include <algorithm>

#include "engine.h"

bool ran_move = false;
bool debug = false;
bool time_left = 0;
bool xboard = false;
bool post = false;
std::ostream& mout(std::cout);


#include "brain.h"

const int MIN_GAMES=1;
int num_lines = 0;

struct WDLTree {
 private:
  WDLTree(const WDLTree&);
  WDLTree operator= (const WDLTree&);
 public:

  struct Node {
    Node * Child, * Sibling;
    int Win,Loss,Draw;
    move M;
    int MagicScore;
    Node():Child(0),Sibling(0),Win(0),Loss(0),Draw(0),M(OUT,OUT),MagicScore(0) {}
    ~Node() {
      delete Child;
      delete Sibling;
    }
  private:
    Node(const Node&);
    Node operator=(const WDLTree::Node&);
  } *Root;

  engine En;
  move_list l;
  typedef move_list::iterator iter;

  WDLTree():Root(0),En(),l() {}

  enum Res {
    WIN, LOSS, DRAW
  };

  move read_move(std::istream& is) {
    std::string str;
    char ch;
    std::string orig;

    bool ignore_tillws = false;
    int variation = 0;

    while(is.get(ch)) {
      if(ch=='\n' or ch==' ') {
        if (ch == '\n') {
          num_lines++;
        }
        ignore_tillws = false;
	if(str=="")  continue; else  break;
      }
      if (ch == '$') {
        ignore_tillws = true;
        continue;
      }
      if (ch == '(') {
        variation++;
        continue;
      }
      if (ch == ')') {
        variation--;
        if (variation < 0) {
          std::cout << "\nSerious Error, variation < 0" << std::endl;
        }
        continue;
      }
      if (variation > 0) {
        continue;
      }
      if (ch == '.') {
        str="";
        continue;
      }
      if (ignore_tillws) {
        continue;
      }
      str+=ch;
    }
    orig = str;
    if(str=="")
      return move(OUT,OUT);

    if(str[str.length()-1]=='+') str.resize(str.length()-1);
    if(str[str.length()-1]=='!') str.resize(str.length()-1);
    if(str[str.length()-1]=='?') str.resize(str.length()-1);
    if(str[str.length()-1]=='#') str.resize(str.length()-1);

    if(str=="0-0" or str=="O-O") {
      if(En.P.A.to_move==WHITE)
	return move(E1,G1);
      else
	return move(E8,G8);
    }
    if(str=="0-0-0" or str=="O-O-O") {
      if(En.P.A.to_move==WHITE)
	return move(E1,C1);
      else
	return move(E8,C8);
    }
    if(str=="1/2-1/2" or str=="1-0" or str=="0-1" or str=="*")
      return move(OUT,OUT);

    piece p1=WPAWN,p2=BPAWN;//Just to avoid warning.

    if(str[str.length()-2]=='=') {
      ///Promotion to ...
      switch(str[str.length()-1]) {
      case 'R': p1=WROOK; p2=BROOK; break;
      case 'N': p1=WKNIGHT; p2=BKNIGHT; break;
      case 'B': p1=WBISHOP; p2=BBISHOP; break;
      case 'Q': p1=WQUEEN; p2=BQUEEN; break;
      }
      for(iter it=l.begin();it!=l.end();)
	if(it->promote!=p1 and it->promote!=p2)
	  it=l.erase(it);
	else
	  ++it;
      str.resize(str.length()-2);// Remove =Q
    }

    switch(str[0]) {
    case 'K': p1=WKING; p2=BKING;     str=1+str.c_str(); break;
    case 'R': p1=WROOK; p2=BROOK;     str=1+str.c_str(); break;
    case 'N': p1=WKNIGHT; p2=BKNIGHT; str=1+str.c_str(); break;
    case 'B': p1=WBISHOP; p2=BBISHOP; str=1+str.c_str(); break;
    case 'Q': p1=WQUEEN; p2=BQUEEN;   str=1+str.c_str(); break;
    default: p1=WPAWN; p2=BPAWN; break;
    }

    for(iter it=l.begin();it!=l.end();)
      if(En.P.CB[it->from]!=p1 and En.P.CB[it->from]!=p2)
	it=l.erase(it);
      else
	++it;

    char to1(str[str.length()-2]), to2(str[str.length()-1]);
    int to((to1-'a')+(to2-'1')*n);

    for(iter it=l.begin();it!=l.end();)
      if(it->to!=to)
	it=l.erase(it);
      else
	++it;

    switch(str.length()) {
    case 2: //e5
      break;
    case 3: //aa3, xb1,3b5
      if(str[0]=='x') break;
      else if(isalpha(str[0])) {
	int file_f=str[0]-'a';
	for(iter it=l.begin();it!=l.end();)
	  if(FILE(it->from)!=file_f)
	    it=l.erase(it);
	  else
	    ++it;
      } else if(isdigit(str[0])) {
	int rank_f=str[0]-'1';
	for(iter it=l.begin();it!=l.end();)
	  if(RANK(it->from)!=rank_f)
	    it=l.erase(it);
	  else
	    ++it;
      }
      break;
    case 4:
      // a1c3
      // fxe5
      // 8xa4
      if(str[1]=='x') {
	if(isalpha(str[0])) {
	  int file_f = str[0]-'a';
	  for(iter it=l.begin();it!=l.end();)
	    if(FILE(it->from)!=file_f)
	      it=l.erase(it);
	    else
	      ++it;
	} else if(isdigit(str[0])) {
	  int rank_f = str[0]-'1';
	  for(iter it=l.begin();it!=l.end();)
	    if(RANK(it->from)!=rank_f)
	      it=l.erase(it);
	    else
	      ++it;
	}
      }
      else {
	int from=(str[0]-'a')+(str[1]-'1')*n;
	for(iter it=l.begin();it!=l.end();)
	  if(it->from!=from)
	    it=l.erase(it);
	  else
	    ++it;
      }
      break;
    default:
      break;
    }
    if(l.size()!=1) {
      std::cout <<"\n" << num_lines << ": Input move: \"" << orig << "\"" << std::endl;
      std::cout <<"Not unique or invalid. "<<l.size()<<std::endl;
      l.clear();
      En.get_moves(l);
      std::cout <<"Valid moves "<<std::endl;
      for(iter it=l.begin();it!=l.end();++it)
	std::cout <<*it<<'\t';
      std::cout <<std::endl;
      std::cout <<En<<std::endl;
      return move(OUT,OUT);
    } else {
      return *(l.begin());
    }
  }
  bool read_header(std::istream& is, Res& res) {
    std::string type, desc;
    char LB, RB, LQ, RQ;
    while(is.get(LB)) {
      if (LB == ' ') { continue; }
      if (LB == '\n') { num_lines++; continue; }
      if(LB!='[') { is.putback(LB); return true; }
      is>>type;
      is>>LQ;
      desc="";
      if (LQ != '\"') {
        // Stray line like:
        // [ 1:0, 33 ]"]
        // ignore those.
        char ch;
        while(is.get(ch)) {
          if (ch == '\n') {
            num_lines++;
            break;
          }
        }
        continue;
      }
      while(is.get(RQ)) {
        if (RQ == '\n') {
          num_lines++;
        }
	if(RQ=='\"') {
	  is.get(RB);
          if (RB == '\n') {
            num_lines++;
          }
	  if(RB==']')
	    break;
	}
	desc+=RQ;
      }
      if(type=="Result") {
	if(desc=="1-0")
	  res=WIN;
	else if(desc=="0-1")
	  res=LOSS;
	else
	  res=DRAW;// Both 1/2-1/2 and *
      }
    }
    return false;
  }
  void construct(std::istream& is) {
    int no_of_games=0;
    int new_allocs=0;

    while(is) {
      ++no_of_games;
      if(no_of_games % 100 == 0) {
	std::cout <<"\rGames: "<<no_of_games<< " : " << num_lines << std::flush;
      }
      Res result;
      if(not read_header(is,result)) break;
      En.reset();

      move m(OUT,OUT), end(OUT,OUT);
      Node **N=&Root;

      l.clear();
      En.get_moves(l);

      bool side = true;

      Node *parent = 0;
      while((m=read_move(is))!=end) {

	while(*N) {
	  if((*N)->M==m) break;
	  N=&((*N)->Sibling);
	}
	if(*N==0) { *N= new Node; (*N)->M=m; new_allocs++; }

	if(side) {
	  switch(result) {
	  case WIN:  (*N)->Win++;  break;
	  case LOSS: (*N)->Loss++; break;
	  case DRAW: (*N)->Draw++; break;
	  }
	} else {
	  switch(result) {
	  case WIN:  (*N)->Loss++; break;
	  case LOSS: (*N)->Win++;  break;
	  case DRAW: (*N)->Draw++; break;
	  }
	}
	side=!side;//flip side.
        parent = *N;
	N=&((*N)->Child);
	
	En.make_move(m);
	l.clear();
	En.get_moves(l);
      }

//     if (parent != 0) {
//       depth = 2;
//       int penscore = analyse(MAX, -MAX);
//       parent->MagicScore = penscore;
//     }
    }


    std::cout <<"\nTotal Games: "<<no_of_games<<'['<<new_allocs<<']'<<'\t' << num_lines<<std::flush;
  }

  int ComputeMagicScore(Node* N, int down) {
    if (down == 10) {
      depth = 2;
      return analyse(MAX, -MAX);
    }
    int best = -2000;

    while(N) {
      En.make_move(N->M);
      if (N->Child != 0) {
        N->MagicScore = ComputeMagicScore(N->Child, down + 1);
      } else {
        depth = 1;
        N->MagicScore = analyse(MAX, -MAX);
      }
      En.undo_move();
      best = std::max(best, N->MagicScore);
      N = N->Sibling;
    }
    return -best;
  }

  void print(std::ostream& os) {
    En.reset();
    ComputeMagicScore(Root, 0);
    std::cout << "[Magic Compute]" << std::flush;
    print(Root,os);
  }
  void print(Node* N, std::ostream& os) {
    while(N) {
      int num_games=N->Win+N->Draw+N->Loss;
      int score = N->MagicScore; //N->Win + N->Draw + N->Loss;
      if(num_games>=MIN_GAMES) {
	os<<N->M<<'['<<score<<']';
	os<<'{'; print(N->Child,os); os<<'}';
      }
      N=N->Sibling;
    }
  }
};

int main() {
  WDLTree T;
  std::ifstream fin("book.pgn");
  std::cout <<"Contructing Tree... "<<std::endl;
  T.construct(fin);
  fin.close();
  std::cout <<"done"<<std::endl;

  std::ofstream fout("open.book");
  std::cout <<"Writing to open.book... "<<std::flush;
  T.print(fout);
  fout.close();
  std::cout <<"done"<<std::endl;

  return 0;
}
